package com.whfc.emp.manager.impl;

import com.whfc.common.enums.DelFlag;
import com.whfc.common.enums.FenceType;
import com.whfc.common.enums.LocaleState;
import com.whfc.common.enums.PostState;
import com.whfc.common.result.PageData;
import com.whfc.common.result.PageVO;
import com.whfc.common.spring.AppContextUtil;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.JSONUtil;
import com.whfc.common.util.PositionUtil;
import com.whfc.emp.dao.*;
import com.whfc.emp.dto.*;
import com.whfc.emp.entity.*;
import com.whfc.emp.enums.*;
import com.whfc.emp.event.HelmetSyncEvent;
import com.whfc.emp.manager.AppEmpDataManager;
import com.whfc.emp.manager.AppEmpWarnRuleManager;
import com.whfc.emp.manager.CommonEmpConfigManager;
import com.whfc.emp.param.AppEmpListParam;
import com.whfc.emp.redis.CardDataRedisDao;
import com.whfc.flux.dao.AppDeviceCardLogDao;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Description 人员数据处理服务
 * @Author hw
 * @Date 2020/12/30 14:52
 * @Version 1.0
 */
@Service
public class AppEmpDataManagerImpl implements AppEmpDataManager {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 有效数据时间
     */
    private static final long VALID_TIMES = 24 * 60 * 60 * 1000;

    /**
     * 超出服务器时间的最大值
     */
    private static final long EXCEED_TIMES = 6 * 60 * 1000;

    /**
     * 有效定位超出时间24小时
     */
    private static final int VALID_GPS_TIMES = 24;

    private static final String LEADER_WORKER_ICON = "people_3.png";
    private static final String NORMAL_WORKER_ICON = "people_4.png";
    private static final String MANAGER_SAFE_ICON = "people_1.png";
    private static final String MANAGER_OTHERS_ICON = "people_2.png";
    private static final String MANAGER_ORANGE_ICON = "people_5.png";

    /**
     * 安全帽图标地址
     */
    //private static final String HELMET_ICON_URL = "https://file.whfciot.com/common/helmet-icon/%s.png";
    private static final String HELMET_ICON_URL = "http://zhgd-file.oss-cn-hangzhou.aliyuncs.com/common/helmet-icon/%s.png";

    private static final String HELMET_ICON = "blue";

    @Autowired
    private AppEmpMapper appEmpMapper;

    @Autowired
    private AppEmpDeviceMapper empDeviceMapper;

    @Autowired
    private AppEmpDataMapper appEmpDataMapper;

    @Autowired
    private AppEmpDayMapper appEmpDayMapper;

    @Autowired
    private AppEmpAttendRecordMapper appEmpAttendRecordMapper;

    @Autowired
    private AppFenceMapper appFenceMapper;

    @Autowired
    private AppEmpWarnRuleManager appEmpWarnRuleManager;

    @Autowired
    private CommonEmpConfigManager commonEmpConfigManager;

    @Autowired
    private CardDataRedisDao cardDataRedisDao;

    @Autowired
    private AppDeviceCardLogDao appDeviceCardLogDao;

    @Autowired
    private AppDeviceCardDataMapper appDeviceCardDataMapper;

    @Autowired
    private AppEmpDeptDayMapper appEmpDeptDayMapper;

    @Override
    public HelmetIconDTO getHelmetIcon(String color) {
        HelmetIconDTO helmetIconDTO = new HelmetIconDTO();
        helmetIconDTO.setIcon(LEADER_WORKER_ICON);
        helmetIconDTO.setIconUrl(String.format(HELMET_ICON_URL, HELMET_ICON));
        if (StringUtils.isNotEmpty(color)) {
            getHelmetIcon(helmetIconDTO, color);
            getHelmetIconUrl(helmetIconDTO, color);
        }
        return helmetIconDTO;
    }

    @Override
    public List<AppDeviceCardLog> getHelmetDataLogByEmpId(Integer empId, Date date, Date startTime, Date endTime) {
        //首次查询,从数据库全量加载当天数据.
        boolean existsCache = cardDataRedisDao.exists(empId, date);
        if (!existsCache) {
            Date dateBegin = DateUtil.getDateBegin(date);
            Date dateEnd = DateUtil.getDateEnd(date);
            List<AppDeviceCardLogDTO> logList = appDeviceCardLogDao.selectHelmetDataLogListByEmpId(empId, dateBegin, dateEnd);
            cardDataRedisDao.addCardDataLog(empId, date, logList);
        }
        return cardDataRedisDao.getCardDataLog(empId, date, startTime, endTime);
    }

    @Override
    public PageVO<AppDeviceCardLog> getHelmetDataLogByEmpIdPage(Integer empId, Date date, Date startTime, Date endTime, Integer pageNum, Integer pageSize) {


        Date dateBegin = DateUtil.getDateBegin(date);
        Date dateEnd = DateUtil.getDateEnd(date);
        PageVO<AppDeviceCardLog> logList = appDeviceCardLogDao.selectHelmetDataLogListByEmpIdPage(empId, dateBegin, dateEnd,pageNum,pageSize);

        return logList;
    }

    @Override
    public PageData<AppDeviceCardLogDTO> getHelmetDataLogByDeviceId(Integer deviceId, Date startTime, Date endTime, Integer pageNum, Integer pageSize) {
        PageData<AppDeviceCardLogDTO> logList = appDeviceCardLogDao.selectHelmetDataLogListByDeviceId(deviceId, startTime, endTime, pageNum, pageSize);
        return logList;
    }

    @Override
    public List<AppDeviceCardLogDTO> getHelmetStatByEmpIds(List<Integer> empIds, Date startTime, Date endTime) {
        List<AppDeviceCardLogDTO> list = appDeviceCardLogDao.selectHelmetDTOByEmpIds(empIds, startTime, endTime);
        return list;
    }

    @Override
    public void addHelmetData(AppDeviceCardLog cardLog) {
        Date serverTime = new Date();
        //查询安全帽绑定的人员信息
        Integer deviceId = cardLog.getDeviceId();
        AppEmpInfoDTO empInfo = empDeviceMapper.selectAppEmpInfoByDeviceId(deviceId);
        Integer empId = empInfo != null ? empInfo.getEmpId() : 0;

        Date logTime = cardLog.getTime();
        //硬件时间晚于服务器时间,并且超出有效范围(6分钟),该时间无效
        if (logTime.after(serverTime) && (logTime.getTime() - serverTime.getTime()) > EXCEED_TIMES) {
            cardLog.setTime(serverTime);
        }
        //硬件时间早于服务器时间,并且超出有效范围(24小时),该时间无效
        else if (logTime.before(serverTime) && (serverTime.getTime() - logTime.getTime() > VALID_TIMES)) {
            cardLog.setTime(serverTime);
        }
        //保存硬件日志数据
        cardLog.setEmpId(empId);
        cardLog.setDelFlag(DelFlag.UNDELETE.getValue());
        appDeviceCardLogDao.insert(cardLog);

        //更新硬件最新数据
        AppDeviceCardData cardData = new AppDeviceCardData();
        BeanUtils.copyProperties(cardLog, cardData);
        appDeviceCardDataMapper.insertOrUpdate(cardData);

        //更新人员信息
        if (empInfo != null) {
            Date logDate = DateUtil.getDate(cardLog.getTime());

            //刷新缓存
            cardDataRedisDao.delete(empId, logDate);

            // 查找人员考勤方式
            AttendType attendType = commonEmpConfigManager.getAttendTypeByEmpId(empId);
            // 闸机考勤
            if (attendType.equals(AttendType.FACEGATE)) {
                logger.info("=============闸机考勤===============");
                //更新人员实时数据
                updateEmpData(empInfo, serverTime, cardLog, null, null);
                //更新人员报警记录
                this.checkWarn(empInfo, cardLog);

            }
            // 电子围栏考勤
            else if (attendType.equals(AttendType.FENCE)) {
                logger.info("=============电子围栏计算考勤===============");
                // gps->计算现场状态
                AppLocaleStateDTO appLocaleStateDTO = this.calculateLocaleState(empInfo, cardLog);
                Integer localeState = appLocaleStateDTO.getLocaleState().getValue();
                Integer attendState = LocaleState.IN.getValue().equals(localeState) ? AttendState.ATTEND.getValue() : AttendState.ABSENCE.getValue();

                //更新人员实时数据
                this.updateEmpData(empInfo, serverTime, cardLog, localeState, attendState);

                //更新人员报警记录
                this.checkWarn(empInfo, cardLog);

                //更新人员每日统计
                this.updateEmpDayData(empInfo, cardLog.getTime(), attendState, localeState);

                //更新人员打卡记录
                this.updateEmpAttendRecord(empInfo, cardLog, appLocaleStateDTO);
            }


            //发布事件
            AppContextUtil.context().publishEvent(new HelmetSyncEvent(empInfo.getDeptId(), cardData));
        }
    }

    @Override
    public void initEmpDayData(Integer deptId, Date date) {
        AppEmpListParam param = new AppEmpListParam();
        param.setDeptId(deptId);
        param.setPostState(PostState.ENTER.getValue());
        List<AppEmpDTO> empList = appEmpMapper.selectByParam(param);
        logger.info("初始化每日人员统计数据,deptId:{},date:{},emps:{}", deptId, date, empList.size());
        List<AppEmpDay> empDayList = new ArrayList<>(empList.size());
        for (AppEmpDTO emp : empList) {
            AppEmpDay empDay = new AppEmpDay();
            empDay.setDeptId(deptId);
            empDay.setEmpId(emp.getEmpId());
            empDay.setDate(date);
            empDay.setWorkRoleId(emp.getWorkRoleId());
            empDayList.add(empDay);
        }
        if (empDayList.size() > 0) {
            appEmpDayMapper.batchInsert(empDayList);
        }
    }

    @Override
    public void statEmpDayData(Integer deptId, Date date) {
        List<AppEmpDay> empDayList = appEmpDayMapper.selectEmpDayListByDeptIdAndDate(deptId, date);
        AttendType attendType = commonEmpConfigManager.getAttendType(deptId);
        logger.info("统计昨日人员统计数据,deptId:{},date:{},emps:{}", deptId, date, empDayList.size());
        for (AppEmpDay empDay : empDayList) {
            if (ObjectUtils.isEmpty(empDay.getStartTime()) || ObjectUtils.isEmpty(empDay.getEndTime())) {
                //电子围栏考勤
                if (AttendType.FACEGATE != attendType) {
                    if (!ObjectUtils.isEmpty(empDay.getHelmetStartTime()) || !ObjectUtils.isEmpty(empDay.getHelmetEndTime())) {
                        empDay.setStartTime(empDay.getHelmetStartTime());
                        empDay.setEndTime(empDay.getHelmetEndTime());
                        empDay.setWorkTimes(empDay.getHelmetTimes());
                        appEmpDayMapper.insertOrUpdate(empDay);
                    }
                }
                //闸机考勤
                else if (AttendType.FENCE != attendType) {
                    if (!ObjectUtils.isEmpty(empDay.getFaceGateStartTime()) || !ObjectUtils.isEmpty(empDay.getFaceGateEndTime())) {
                        empDay.setStartTime(empDay.getFaceGateStartTime());
                        empDay.setEndTime(empDay.getFaceGateEndTime());
                        empDay.setWorkTimes(empDay.getFaceGateTimes());
                        appEmpDayMapper.insertOrUpdate(empDay);
                    }
                }

            }
        }
    }

    @Override
    public AppEmpDay getEmpDay(Integer empId, Date date) {
        AppEmpDay appEmpDay = appEmpDayMapper.selectByEmpIdAndDate(empId, date);
        if (appEmpDay == null) {
            AppEmp appEmp = appEmpMapper.selectByPrimaryKey(empId);
            appEmpDay = new AppEmpDay();
            appEmpDay.setDeptId(appEmp.getDeptId());
            appEmpDay.setEmpId(empId);
            appEmpDay.setWorkRoleId(appEmp.getWorkRoleId());
            appEmpDay.setDate(date);
            appEmpDay.setAttendState(AttendState.ABSENCE.getValue());
            appEmpDayMapper.insertSelective(appEmpDay);
        }
        return appEmpDay;
    }

    @Override
    public void statDeptDay(Integer deptId, Date date) {

    }

    /**
     * 更新人员实时数据
     *
     * @param empInfo
     * @param serverTime
     * @param log
     * @param localeState
     * @param attendState
     */
    private void updateEmpData(AppEmpInfoDTO empInfo, Date serverTime, AppDeviceCardLog log, Integer localeState, Integer attendState) {

        Integer empId = empInfo.getEmpId();
        Integer deviceId = empInfo.getDeviceId();
        AppEmpData empData = appEmpDataMapper.selectByEmpId(empId);
        if (empData == null) {
            empData = new AppEmpData();
        }

        //验证硬件时间
        Date logTime = log.getTime();
        if ((empData.getTime() != null && logTime.before(empData.getTime()))) {
            logger.info("硬件时间不正确,deviceId:{},empId:{},dataTime:{},logTime:{}", deviceId, empId, empData.getTime(), logTime);
            return;
        }
        empData.setTime(logTime);

        // 清除24小时前定位信息
        Date gpsTime = empData.getGpsTime();
        if (gpsTime != null && DateUtil.addHours(logTime, -VALID_GPS_TIMES).after(gpsTime)) {
            empData.setLng(0.0);
            empData.setLat(0.0);
            empData.setLngWgs84(0.0);
            empData.setLatWgs84(0.0);
            empData.setGpsTime(logTime);
        }

        //验证经纬度
        String polygonWKT = commonEmpConfigManager.getPolygonWKT(empInfo.getDeptId());
        if (PositionUtil.contains(FenceType.POLYGON.value(), polygonWKT, null, log.getLat(), log.getLng()) && PosState.VALID.getValue().equals(log.getPosState())) {
            empData.setLng(log.getLng());
            empData.setLat(log.getLat());
            empData.setLngWgs84(log.getLngWgs84());
            empData.setLatWgs84(log.getLatWgs84());
            empData.setGpsTime(logTime);
            //现场状态
            empData.setLocaleState(localeState);
        }
        //验证电量
        if (log.getBatteryPower() != null && log.getBatteryPower() > 0 && log.getBatteryPower() <= 100) {
            empData.setBatteryPower(log.getBatteryPower());
        }
        //出勤状态
        if (AttendState.ATTEND.getValue().equals(attendState)) {
            empData.setAttendState(attendState);
        }
        empData.setEmpId(empId);
        empData.setDeviceId(deviceId);
        empData.setNetState(NetState.ONLINE.getValue());
        empData.setUpdateTime(serverTime);
        appEmpDataMapper.insertOrUpdate(empData);
    }

    /**
     * 更新人员报警记录
     *
     * @param empInfo
     * @param log
     */
    private void checkWarn(AppEmpInfoDTO empInfo, AppDeviceCardLog log) {
        Double lat = log.getLat();
        Double lng = log.getLng();
        EmpWarnCheckDTO warnCheckDTO = new EmpWarnCheckDTO();
        if (empInfo != null){
            String polygonWKT = commonEmpConfigManager.getRestAreaPolygonWKT(empInfo.getDeptId());
            if (lat == null || lat == 0 || lng == null || lng == 0) {
                // 在室内不检测报警
                return;
            }
            // 未设置休息区,需检测报警
            if (!StringUtils.isBlank(polygonWKT) && PositionUtil.contains(FenceType.POLYGON.value(), polygonWKT, null, lat, lng)) {
                // 定位在办公室休息区 不检测报警
                return;
            }

            Integer empId = empInfo.getEmpId();
            warnCheckDTO.setEmpId(empId);
        }

        Date warnTime = DateUtil.getDateTime(log.getTime());

        warnCheckDTO.setAlarmDoff(log.getAlarmDoff());
        warnCheckDTO.setAlarmDrop(log.getAlarmDrop());
        warnCheckDTO.setAlarmSos(log.getAlarmSos());
        warnCheckDTO.setAlarmCrash(log.getAlarmCrash());
        warnCheckDTO.setAlarmStill(log.getAlarmStill());
        warnCheckDTO.setLeaveStill(log.getLeaveStill());
        warnCheckDTO.setTriggerTime(warnTime);

        warnCheckDTO.setLat(lat);
        warnCheckDTO.setLng(lng);
        warnCheckDTO.setFence(log.getFence());
        warnCheckDTO.setExtraInfo(log.getExtraInfo());
        appEmpWarnRuleManager.checkWarnRule(warnCheckDTO);
    }

    @Override
    public void checkWarn(AppDeviceCardLog log) {
        Integer deviceId = log.getDeviceId();
        AppEmpInfoDTO empInfo = empDeviceMapper.selectAppEmpInfoByDeviceId(deviceId);
        if (empInfo!=null) {
            this.checkWarn(empInfo,log);
        }
    }

    /**
     * 比较电子围栏
     *
     * @param empInfo
     * @param log
     * @return
     */
    private AppLocaleStateDTO calculateLocaleState(AppEmpInfoDTO empInfo, AppDeviceCardLog log) {
        AppLocaleStateDTO appLocaleStateDTO = new AppLocaleStateDTO();
        appLocaleStateDTO.setAttendType(AttendType.FENCE);

        Integer deptId = empInfo.getDeptId();
        Integer empId = empInfo.getEmpId();
        if (log.getLng() > 0D && log.getLat() > 0D) {
            List<String> fenceNameList = appFenceMapper.selectFenceIdListContainsGps(deptId, empId, log.getLng(), log.getLat());
            logger.info("判断是否在电子围栏内,lng:{},lat:{},fenceIdList:{}", log.getLng(), log.getLat(), fenceNameList);
            LocaleState localeState = fenceNameList.size() > 0 ? LocaleState.IN : LocaleState.OUT;
            appLocaleStateDTO.setLocaleState(localeState);
            if (fenceNameList.size() > 0) {
                appLocaleStateDTO.setName(fenceNameList.get(0));
            }
            return appLocaleStateDTO;
        }
        appLocaleStateDTO.setLocaleState(LocaleState.OUT);
        return appLocaleStateDTO;
    }

    /**
     * 比较电子围栏
     *
     * @param
     * @param
     * @return
     */
    @Override
    public AppLocaleStateDTO calculateLocaleState(Integer deptId, Integer empId,Double lng,Double lat,AttendType attendType) {
        logger.info("检验电子围栏deptId={},empId={},lng={},lat={}",deptId,empId,lng,lat);
        AppLocaleStateDTO appLocaleStateDTO = new AppLocaleStateDTO();
        appLocaleStateDTO.setAttendType(attendType);

        if (lng > 0D && lat > 0D) {
            List<String> fenceNameList = appFenceMapper.selectFenceIdListContainsGps(deptId, empId, lng, lat);
            logger.info("判断是否在电子围栏内,lng:{},lat:{},fenceIdList:{}", lng, lat, fenceNameList);
            LocaleState localeState = fenceNameList.size() > 0 ? LocaleState.IN : LocaleState.OUT;
            appLocaleStateDTO.setLocaleState(localeState);
            if (fenceNameList.size() > 0) {
                appLocaleStateDTO.setName(fenceNameList.get(0));
            }
            return appLocaleStateDTO;
        }
        appLocaleStateDTO.setLocaleState(LocaleState.OUT);
        return appLocaleStateDTO;
    }


    /**
     * 更新人员的打卡记录
     *
     * @param empInfo
     * @param log
     * @param appLocaleStateDTO
     */
    private void updateEmpAttendRecord(AppEmpInfoDTO empInfo, AppDeviceCardLog log, AppLocaleStateDTO appLocaleStateDTO) {
        logger.info("==============电子围栏更新员工打卡记录============");
        //验证经纬度
        if (log.getLng() <= 0 || log.getLat() <= 0) {
            return;
        }
        LocaleState localeState = appLocaleStateDTO.getLocaleState();
        Date time = log.getTime();
        Integer empId = empInfo.getEmpId();

        // 查找上一条进出记录
        Integer direction = localeState.getValue();
        Date date = DateUtil.getDate(time);
        AppEmpAttendRecord attendRecord = appEmpAttendRecordMapper.selectLastAttendRecord(empId, AttendType.FENCE.getValue(), date);
        if (attendRecord == null || !attendRecord.getDirection().equals(direction)) {
            // 和上一条记录方向不一致  插入记录
            AppEmpAttendRecord empAttendRecord = new AppEmpAttendRecord();
            empAttendRecord.setEmpId(empId);
            empAttendRecord.setTime(time);
            empAttendRecord.setType(AttendType.FENCE.getValue());
            empAttendRecord.setDeptId(empInfo.getDeptId());
            empAttendRecord.setDirection(direction);
            empAttendRecord.setName(appLocaleStateDTO.getName());
            appEmpAttendRecordMapper.insertSelective(empAttendRecord);
        }

    }

    /**
     * 更新人员每日数据统计
     *
     * @param empInfo
     * @param date
     * @param attendState
     * @param localeState
     */
    private void updateEmpDayData(AppEmpInfoDTO empInfo, Date date, Integer attendState, Integer localeState) {
        logger.info("empInfo:{},date:{},attendState:{},localeState:{}", JSONUtil.toString(empInfo), date, attendState, localeState);
        Integer empId = empInfo.getEmpId();
        Integer deptId = empInfo.getDeptId();
        AppEmpDay empDay = appEmpDayMapper.selectByEmpIdAndDate(empId, date);
        if (empDay == null) {
            empDay = new AppEmpDay();
        }
        empDay.setDeptId(deptId);
        empDay.setEmpId(empId);
        empDay.setDate(date);

        // 出勤状态
        if (AttendState.ATTEND.getValue().equals(attendState)) {
            empDay.setAttendState(attendState);
            empDay.setAttendType(AttendType.FENCE.getValue());
        }

        // 出勤时长
        AppEmpAttendRecord appEmpAttendRecord = appEmpAttendRecordMapper.selectLastAttendRecord(empId, AttendType.FENCE.getValue(), DateUtil.getDate(date));
        logger.info("appEmpAttendRecord:{}", JSONUtil.toString(appEmpAttendRecord));
        if (appEmpAttendRecord == null && Direction.IN.getValue().equals(localeState)) {
            empDay.setHelmetStartTime(date);
            empDay.setStartTime(date);
        } else if (appEmpAttendRecord != null && Direction.IN.getValue().equals(appEmpAttendRecord.getDirection()) && Direction.OUT.getValue().equals(localeState)) {
            empDay.setHelmetEndTime(date);
            empDay.setEndTime(date);
            int times = calculateTime(appEmpAttendRecord.getTime(), date);
            empDay.setHelmetTimes(empDay.getHelmetTimes() != null ? empDay.getHelmetTimes() + times : times);
            empDay.setWorkTimes(empDay.getHelmetTimes());
        }

        appEmpDayMapper.insertOrUpdate(empDay);
    }

    /**
     * 计算时长
     *
     * @param startTime
     * @param endTime
     * @return 时长(单位 : 秒)
     */
    private int calculateTime(Date startTime, Date endTime) {
        int times = (int) ((endTime.getTime() - startTime.getTime()) / 1000);
        return times;
    }

    /**
     * 获取安全帽图标
     *
     * @param helmetIconDTO
     * @param color
     */
    private void getHelmetIcon(HelmetIconDTO helmetIconDTO, String color) {
        String icon = LEADER_WORKER_ICON;
        if (StringUtils.isNotEmpty(color)) {
            switch (color) {
                case "yellow":
                    icon = NORMAL_WORKER_ICON;
                    break;
                case "white":
                    icon = MANAGER_SAFE_ICON;
                    break;
                case "red":
                    icon = MANAGER_OTHERS_ICON;
                    break;
                case "blue":
                    icon = LEADER_WORKER_ICON;
                    break;
                case "orange":
                    icon = MANAGER_ORANGE_ICON;
                    break;
            }
        }
        helmetIconDTO.setIcon(icon);
    }

    /**
     * 获取安全帽图标
     *
     * @param helmetIconDTO
     * @param color
     */
    private void getHelmetIconUrl(HelmetIconDTO helmetIconDTO, String color) {
        String url = String.format(HELMET_ICON_URL, HELMET_ICON);
        if (StringUtils.isNotEmpty(color)) {
            url = String.format(HELMET_ICON_URL, color);
        }
        helmetIconDTO.setIconUrl(url);
    }
}
